package com.yuqq;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.*;
import java.util.*;

public class ExcelDeduplication3 {
    // 是否忽略大小写（根据需求调整，如 true 则统一转大写/小写）
    private static final boolean IGNORE_CASE = true;

    public static void main(String[] args) {
        // 输入 Excel 文件路径，需替换为实际路径
        String inputPath = "src/main/resources/input.xlsx";
        // 输出 Excel 文件路径，需替换为实际路径
        String outputPath = "src/main/resources/output.xlsx";

        try (Workbook workbook = new XSSFWorkbook(new FileInputStream(inputPath))) {
            Sheet sheet = workbook.getSheetAt(0);
            Map<String, List<RowInfo>> rowMap = new HashMap<>();

            // 1. 遍历行，记录内容和元信息
            for (int rowIndex = 0; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
                Row row = sheet.getRow(rowIndex);
                if (row == null) continue;

                String rowKey = getNormalizedRowContent(row); // 优化后的内容拼接
                Cell secondCell = row.getCell(1); // 第二列索引（B列）
                boolean hasSecondValue = (secondCell != null
                        && secondCell.getCellType() != CellType.BLANK);

                rowMap.computeIfAbsent(rowKey, k -> new ArrayList<>())
                        .add(new RowInfo(rowIndex, hasSecondValue));
            }

            // 2. 标记需删除的行（按三种逻辑处理）
            List<Integer> rowsToDelete = new ArrayList<>();
            ////////////////////////////
            for (Map.Entry<String, List<RowInfo>> entry : rowMap.entrySet()) {
                List<RowInfo> rows = entry.getValue();
                if (rows.size() <= 1) continue; // 无重复，跳过

                // 检查是否同时存在第二列有值和无值的情况
                boolean hasValue = false;
                boolean hasNoValue = false;
                for (RowInfo rowInfo : rows) {
                    if (rowInfo.hasSecondColumnValue) hasValue = true;
                    else hasNoValue = true;

                    if (hasValue && hasNoValue) break; // 已确定存在混合情况，提前退出
                }

                // 如果同时存在有值和无值的情况，删除所有无值的行
                if (hasValue && hasNoValue) {
                    for (RowInfo rowInfo : rows) {
                        if (!rowInfo.hasSecondColumnValue) {
                            rowsToDelete.add(rowInfo.rowIndex);
                        }
                    }
                }
            }
            /////////////////////
            for (List<RowInfo> rowInfos : rowMap.values()) {
                if (rowInfos.size() <= 1) continue; // 无重复，跳过

                // 优先处理「有值 + 无值」混合的情况（逻辑 3）
                boolean hasMixed = hasMixedSecondColumn(rowInfos);
                if (hasMixed) {
                    markMixedRows(rowInfos, rowsToDelete);
                    continue;
                }

                // 处理「纯有值」或「纯无值」重复（逻辑 1/2）
                markSameSecondColumnRows(rowInfos, rowsToDelete);
            }

            // 3. 执行删除（从后往前删，避免行号错乱）
            rowsToDelete.sort(Comparator.reverseOrder());
            for (int rowIndex : rowsToDelete) {
                deleteRow(sheet, rowIndex);
            }

            // 4. 输出结果
            try (FileOutputStream fos = new FileOutputStream(outputPath)) {
                workbook.write(fos);
            }
            System.out.println("去重完成！结果已保存至: " + outputPath);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // ======================== 核心工具方法 ========================
    /**
     * 归一化行内容：
     * 1. trim 所有单元格内容（去首尾空格）
     * 2. 空单元格统一标记为 [EMPTY]
     * 3. 支持忽略大小写（统一转大写）
     * 4. 处理数值/日期格式（转为文本）
     */
    private static String getNormalizedRowContent(Row row) {
        StringBuilder content = new StringBuilder();
        int lastCellNum = row.getLastCellNum();
        for (int cellIndex = 0; cellIndex < lastCellNum; cellIndex++) {
            Cell cell = row.getCell(cellIndex);
            String cellValue = getNormalizedCellValue(cell);

            // 用特殊分隔符（如 || ）避免单元格内容包含 \t 时误判
            content.append(cellValue).append("||");
        }
        return content.toString();
    }

    /**
     * 归一化单元格内容（处理空值、格式、大小写）
     */
    private static String getNormalizedCellValue(Cell cell) {
        if (cell == null || cell.getCellType() == CellType.BLANK) {
            return "[EMPTY]"; // 空单元格统一标记
        }

        String value;
        switch (cell.getCellType()) {
            case STRING:
                value = cell.getStringCellValue().trim(); // 去首尾空格
                break;
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    // 日期转为统一格式（如 yyyy-MM-dd）
                    value = cell.getLocalDateTimeCellValue().format(
                            java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd")
                    );
                } else {
                    // 数值转文本（避免 123 和 123.0 被判定为不同）
                    value = String.valueOf(cell.getNumericCellValue()).trim();
                }
                break;
            case BOOLEAN:
                value = String.valueOf(cell.getBooleanCellValue()).trim();
                break;
            case FORMULA:
                // 公式结果按实际值处理
                // 公式结果按实际值处理
                switch (cell.getCachedFormulaResultType()) {
                    case NUMERIC:
                        value = String.valueOf(cell.getNumericCellValue()).trim();
                        break;
                    case STRING:
                        value = cell.getStringCellValue().trim();
                        break;
                    case BOOLEAN:
                        value = String.valueOf(cell.getBooleanCellValue()).trim();
                        break;
                    case ERROR:
                        value = "[ERROR]";
                        break;
                    default:
                        value = "";
                }
                break;
            default:
                value = "";
        }

        // 忽略大小写（统一转大写）
        return IGNORE_CASE ? value.toUpperCase() : value;
    }

    /**
     * 判断是否存在「第二列有值 + 无值」混合的行
     */
    private static boolean hasMixedSecondColumn(List<RowInfo> rowInfos) {
        boolean hasValue = false, hasNoValue = false;
        for (RowInfo info : rowInfos) {
            if (info.hasSecondColumnValue) hasValue = true;
            else hasNoValue = true;
            if (hasValue && hasNoValue) return true; // 提前终止
        }
        return false;
    }

    /**
     * 标记「有值 + 无值」混合的行（逻辑 3：删无值的行）
     */
    private static void markMixedRows(List<RowInfo> rowInfos, List<Integer> rowsToDelete) {
        for (RowInfo info : rowInfos) {
            if (!info.hasSecondColumnValue) { // 第二列无值的行
                rowsToDelete.add(info.rowIndex);
            }
        }
    }

    /**
     * 标记「纯有值」或「纯无值」的重复行（逻辑 1/2：删除最后一行外的所有行）
     */
    private static void markSameSecondColumnRows(List<RowInfo> rowInfos, List<Integer> rowsToDelete) {
        // 保留最后一行，删除前面的行
        for (int i = 0; i < rowInfos.size() - 1; i++) {
            rowsToDelete.add(rowInfos.get(i).rowIndex);
        }
    }

    /**
     * 安全删除行（兼容 Excel 行移动逻辑）
     */
    private static void deleteRow(Sheet sheet, int rowIndex) {
        int lastRowNum = sheet.getLastRowNum();
        if (rowIndex >= 0 && rowIndex < lastRowNum) {
            sheet.shiftRows(rowIndex + 1, lastRowNum, -1);
        } else if (rowIndex == lastRowNum) {
            Row row = sheet.getRow(rowIndex);
            if (row != null) {
                sheet.removeRow(row);
            }
        }
    }

    // ======================== 辅助类 ========================
    static class RowInfo {
        int rowIndex;
        boolean hasSecondColumnValue;

        public RowInfo(int rowIndex, boolean hasSecondColumnValue) {
            this.rowIndex = rowIndex;
            this.hasSecondColumnValue = hasSecondColumnValue;
        }
    }
}
